package log;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;

import path.PathManager;
import time.TimeUtil;
import conf.Configure;

public class Logger {
	protected static Logger smLogger = null;
	protected static final String kFILESUFFIX = ".log";

	private static final String LOG_FILE_NAME = "current.log";
	private static final String ERROR_FILE_NAME = "error.log";

	protected File logDir;

	protected int logFileLevel = 1;
	protected int logStdoutLevel = 1;
	private static final String FORMATTER = "yyyyMMdd_HHmmss";

	protected int mLogLimit = 1024;
	protected int counter = 0;

	protected PrintWriter mOut;

	public static void trace(String message) {
		log(LogLevel.TRACE, message);
	}

	public static void warn(String message) {
		log(LogLevel.WARNING, message);
	}

	public static void debug(String message) {
		log(LogLevel.DEBUG, message);
	}

	public static void info(String message) {
		log(LogLevel.INFO, message);
	}

	public static void error(Throwable e, String message) {
		log(LogLevel.ERROR, e, message);
	}

	public static void error(String message) {
		log(LogLevel.ERROR, message);
	}
	
	public static void error(Throwable e) {
		log(LogLevel.ERROR, "");
	}

	public static void log(LogLevel logLevel, String message) {
		getInstance().logMessage(logLevel, null, message);
	}

	public static void log(LogLevel logLevel, Throwable e, String message) {
		getInstance().logMessage(logLevel, e, message);
	}

	protected void logMessage(LogLevel logLevel, Throwable throwable, String message) {
		try {
			doLogMessage(logLevel, throwable, message);
		} catch (Throwable t) {
			String stdoutMessage = TimeUtil.getDateTimeString() + "\t" + logLevel.getName() + "\t" + "Error during logging for message: " + message;
			System.out.println(stdoutMessage + ": \n");
			throwable.printStackTrace();
			System.out.println("Exception caught from the logger code:");
			t.printStackTrace();
		}
	}

	protected void doLogMessage(LogLevel logLevel, Throwable throwable, String message) {
		boolean okToFile = logLevel.getId() <= logFileLevel;
		boolean okToStdout = logLevel.getId() <= logStdoutLevel;
		if (!okToFile && !okToStdout) {
			return;
		}

		String msg = TimeUtil.getDateTimeString() + "\t" + logLevel.getName() + "\t" + message;
		if (okToStdout) {
			System.out.println(msg);
			if (throwable != null) {
				throwable.printStackTrace();
			}
		}
		if (okToFile) {
			try {
				checkLogFile();
				mOut.println(msg);
				if (throwable != null) {
					throwable.printStackTrace(mOut);
					mOut.println("");
					counter += 9;
				}
				mOut.flush();
			} catch (Exception e) {
				e.printStackTrace();
			}
			counter++;
		}

		if (logLevel == LogLevel.ERROR) {
			File errorLogFile = new File(logDir, ERROR_FILE_NAME);
			try {
				PrintWriter pw = new PrintWriter(new FileOutputStream(errorLogFile, true), false);
				pw.println(msg);
				if (throwable != null) {
					throwable.printStackTrace(pw);
				}
				pw.close();
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			}
		}
	}

	protected void checkLogFile() {
		if (counter < 30) {
			return;
		}

		synchronized (this) {
			File thisFile = new File(logDir, LOG_FILE_NAME);
			if (thisFile.length() > (1024 * mLogLimit)) {
				if (mOut != null) {
					mOut.flush();
					mOut.close();
					mOut = null;
				}
				String ts = new SimpleDateFormat(FORMATTER).format(new Date());
				String fileName = "log_" + ts + kFILESUFFIX;
				File newFile = new File(logDir, fileName);
				File oldFile = new File(logDir, LOG_FILE_NAME);
				oldFile.renameTo(newFile);
				open();
			}
			counter = 0;
		}
	}

	private Logger() {
		init();
		open();
	}

	private static Logger getInstance() {
		if (smLogger == null) {
			smLogger = new Logger();
		}
		return smLogger;
	}

	private void init() {
		if(logDir ==null ){
			logDir = PathManager.getInstance().getLogPath();
		}
		
		if (!logDir.exists()) {
			logDir.mkdirs();
		}

		String fileLev = Configure.getProperty(Configure.LOG_FILE_LEVEL);
		if (fileLev != null && fileLev.length() > 0){
			logFileLevel = Integer.parseInt(fileLev);
		}

		String stdLev = Configure.getProperty(Configure.LOG_STDOUT_LEVEL);
		if (stdLev != null && stdLev.length() > 0){
			logStdoutLevel = Integer.parseInt(stdLev);
		}

		String fileSize = Configure.getProperty(Configure.LOG_FILE_SIZE);
		mLogLimit = Integer.parseInt(fileSize);
	}

	private void open() {
		try {
			File logFile = new File(logDir, LOG_FILE_NAME);
			mOut = new PrintWriter(new FileOutputStream(logFile, true), false);
		} catch (IOException ioe) {
			System.out.println("Cannot open a log file: " + LOG_FILE_NAME + " in directory: " + logDir.getAbsolutePath());
			ioe.printStackTrace();
		}
	}
}
